// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This package defines constants representing the lexical
// tokens of the Go programming language and basic operations
// on tokens (printing, predicates).
//
package token

import (
	"fmt";
	"strconv";
)


// Token is the set of lexical tokens of the Go programming language.
type Token int

// The list of tokens.
const (
	// Special tokens
	ILLEGAL	Token	= iota;
	EOF;
	COMMENT;

	literal_beg;
	// Identifiers and basic type literals
	// (these tokens stand for classes of literals)
	IDENT;	// main
	INT;	// 12345
	FLOAT;	// 123.45
	CHAR;	// 'a'
	STRING;	// "abc"
	literal_end;

	operator_beg;
	// Operators and delimiters
	ADD;	// +
	SUB;	// -
	MUL;	// *
	QUO;	// /
	REM;	// %

	AND;		// &
	OR;		// |
	XOR;		// ^
	SHL;		// <<
	SHR;		// >>
	AND_NOT;	// &^

	ADD_ASSIGN;	// +=
	SUB_ASSIGN;	// -=
	MUL_ASSIGN;	// *=
	QUO_ASSIGN;	// /=
	REM_ASSIGN;	// %=

	AND_ASSIGN;	// &=
	OR_ASSIGN;	// |=
	XOR_ASSIGN;	// ^=
	SHL_ASSIGN;	// <<=
	SHR_ASSIGN;	// >>=
	AND_NOT_ASSIGN;	// &^=

	LAND;	// &&
	LOR;	// ||
	ARROW;	// <-
	INC;	// ++
	DEC;	// --

	EQL;	// ==
	LSS;	// <
	GTR;	// >
	ASSIGN;	// =
	NOT;	// !

	NEQ;		// !=
	LEQ;		// <=
	GEQ;		// >=
	DEFINE;		// :=
	ELLIPSIS;	// ...

	LPAREN;	// (
	LBRACK;	// [
	LBRACE;	// {
	COMMA;	// ,
	PERIOD;	// .

	RPAREN;		// )
	RBRACK;		// ]
	RBRACE;		// }
	SEMICOLON;	// ;
	COLON;		// :
	operator_end;

	keyword_beg;
	// Keywords
	BREAK;
	CASE;
	CHAN;
	CONST;
	CONTINUE;

	DEFAULT;
	DEFER;
	ELSE;
	FALLTHROUGH;
	FOR;

	FUNC;
	GO;
	GOTO;
	IF;
	IMPORT;

	INTERFACE;
	MAP;
	PACKAGE;
	RANGE;
	RETURN;

	SELECT;
	STRUCT;
	SWITCH;
	TYPE;
	VAR;
	keyword_end;
)


// At the moment we have no array literal syntax that lets us describe
// the index for each element - use a map for now to make sure they are
// in sync.
var tokens = map[Token]string{
	ILLEGAL: "ILLEGAL",

	EOF: "EOF",
	COMMENT: "COMMENT",

	IDENT: "IDENT",
	INT: "INT",
	FLOAT: "FLOAT",
	CHAR: "CHAR",
	STRING: "STRING",

	ADD: "+",
	SUB: "-",
	MUL: "*",
	QUO: "/",
	REM: "%",

	AND: "&",
	OR: "|",
	XOR: "^",
	SHL: "<<",
	SHR: ">>",
	AND_NOT: "&^",

	ADD_ASSIGN: "+=",
	SUB_ASSIGN: "-=",
	MUL_ASSIGN: "*=",
	QUO_ASSIGN: "/=",
	REM_ASSIGN: "%=",

	AND_ASSIGN: "&=",
	OR_ASSIGN: "|=",
	XOR_ASSIGN: "^=",
	SHL_ASSIGN: "<<=",
	SHR_ASSIGN: ">>=",
	AND_NOT_ASSIGN: "&^=",

	LAND: "&&",
	LOR: "||",
	ARROW: "<-",
	INC: "++",
	DEC: "--",

	EQL: "==",
	LSS: "<",
	GTR: ">",
	ASSIGN: "=",
	NOT: "!",

	NEQ: "!=",
	LEQ: "<=",
	GEQ: ">=",
	DEFINE: ":=",
	ELLIPSIS: "...",

	LPAREN: "(",
	LBRACK: "[",
	LBRACE: "{",
	COMMA: ",",
	PERIOD: ".",

	RPAREN: ")",
	RBRACK: "]",
	RBRACE: "}",
	SEMICOLON: ";",
	COLON: ":",

	BREAK: "break",
	CASE: "case",
	CHAN: "chan",
	CONST: "const",
	CONTINUE: "continue",

	DEFAULT: "default",
	DEFER: "defer",
	ELSE: "else",
	FALLTHROUGH: "fallthrough",
	FOR: "for",

	FUNC: "func",
	GO: "go",
	GOTO: "goto",
	IF: "if",
	IMPORT: "import",

	INTERFACE: "interface",
	MAP: "map",
	PACKAGE: "package",
	RANGE: "range",
	RETURN: "return",

	SELECT: "select",
	STRUCT: "struct",
	SWITCH: "switch",
	TYPE: "type",
	VAR: "var",
}


// String returns the string corresponding to the token tok.
// For operators, delimiters, and keywords the string is the actual
// token character sequence (e.g., for the token ADD, the string is
// "+"). For all other tokens the string corresponds to the token
// constant name (e.g. for the token IDENT, the string is "IDENT").
//
func (tok Token) String() string {
	if str, exists := tokens[tok]; exists {
		return str
	}
	return "token(" + strconv.Itoa(int(tok)) + ")";
}


// A set of constants for precedence-based expression parsing.
// Non-operators have lowest precedence, followed by operators
// starting with precedence 1 up to unary operators. The highest
// precedence corresponds serves as "catch-all" precedence for
// selector, indexing, and other operator and delimiter tokens.
//
const (
	LowestPrec	= 0;	// non-operators
	UnaryPrec	= 7;
	HighestPrec	= 8;
)


// Precedence returns the operator precedence of the binary
// operator op. If op is not a binary operator, the result
// is LowestPrecedence.
//
func (op Token) Precedence() int {
	switch op {
	case LOR:
		return 1
	case LAND:
		return 2
	case ARROW:
		return 3
	case EQL, NEQ, LSS, LEQ, GTR, GEQ:
		return 4
	case ADD, SUB, OR, XOR:
		return 5
	case MUL, QUO, REM, SHL, SHR, AND, AND_NOT:
		return 6
	}
	return LowestPrec;
}


var keywords map[string]Token

func init() {
	keywords = make(map[string]Token);
	for i := keyword_beg + 1; i < keyword_end; i++ {
		keywords[tokens[i]] = i
	}
}


// Lookup maps an identifier to its keyword token or IDENT (if not a keyword).
//
func Lookup(ident []byte) Token {
	// TODO Maps with []byte key are illegal because []byte does not
	//      support == . Should find a more efficient solution eventually.
	if tok, is_keyword := keywords[string(ident)]; is_keyword {
		return tok
	}
	return IDENT;
}


// Predicates

// IsLiteral returns true for tokens corresponding to identifiers
// and basic type literals; returns false otherwise.
//
func (tok Token) IsLiteral() bool	{ return literal_beg < tok && tok < literal_end }

// IsOperator returns true for tokens corresponding to operators and
// delimiters; returns false otherwise.
//
func (tok Token) IsOperator() bool	{ return operator_beg < tok && tok < operator_end }

// IsKeyword returns true for tokens corresponding to keywords;
// returns false otherwise.
//
func (tok Token) IsKeyword() bool	{ return keyword_beg < tok && tok < keyword_end }


// Token source positions are represented by a Position value.
// A Position is valid if the line number is > 0.
//
type Position struct {
	Filename	string;	// filename, if any
	Offset		int;	// byte offset, starting at 0
	Line		int;	// line number, starting at 1
	Column		int;	// column number, starting at 1 (character count)
}


// Pos is an accessor method for anonymous Position fields.
// It returns its receiver.
//
func (pos *Position) Pos() Position	{ return *pos }


// IsValid returns true if the position is valid.
func (pos *Position) IsValid() bool	{ return pos.Line > 0 }


func (pos Position) String() string {
	s := pos.Filename;
	if pos.IsValid() {
		if s != "" {
			s += ":"
		}
		s += fmt.Sprintf("%d:%d", pos.Line, pos.Column);
	}
	if s == "" {
		s = "???"
	}
	return s;
}
